package com.fsh.lingsp.common.websocket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.NettyRuntime;
import io.netty.util.concurrent.Future;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * 作者：fsh
 * 日期：2024/02/22
 * <p>
 * 描述：Netty Web 套接字服务器
 */
@Slf4j
@Configuration
public class NettyWebSocketServer {
    // 定义一个静态常量，表示WebSocket服务器监听的端口号为8090
    public static final int WEB_SOCKET_PORT = 8090;

    // 创建一个静态的NettyWebSocketServerHandler实例，作为WebSocket服务器处理器
    public static final NettyWebSocketServerHandler NETTY_WEB_SOCKET_SERVER_HANDLER = new NettyWebSocketServerHandler();

    // 创建bossGroup线程池执行器，用于接受客户端的连接
    private EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    // 创建workerGroup线程池执行器，用于处理已接受的连接（如读写操作）
    private EventLoopGroup workerGroup = new NioEventLoopGroup(NettyRuntime.availableProcessors());


    /**
     * 使用@PostConstruct注解标记的方法，在依赖注入完成后自动执行
     */
    @PostConstruct
    public void start() throws InterruptedException {
        // 启动服务器
        run();
    }

    /**
     * 运行方法，用于启动WebSocket服务器
     */
    public void run() throws InterruptedException {
        // 创建服务器启动引导对象
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        // 设置bossGroup和workerGroup到引导对象
        serverBootstrap.group(bossGroup, workerGroup)
                // 设置NIO传输的Channel类型
                .channel(NioServerSocketChannel.class)
                // 设置TCP参数，SO_BACKLOG表示等待连接的最大数量
                .option(ChannelOption.SO_BACKLOG, 128)
                // 开启TCP keepalive机制
                .option(ChannelOption.SO_KEEPALIVE, true)
                // 为bossGroup添加日志处理器，打印INFO级别的日志
                .handler(new LoggingHandler(LogLevel.INFO))
                // 设置子Channel的初始化处理器
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        // 获取ChannelPipeline，用于添加处理器
                        ChannelPipeline pipeline = socketChannel.pipeline();
                        // （注释掉的代码）设置心跳检测，如果30秒内客户端没有发送心跳，则关闭连接
//                         pipeline.addLast(new IdleStateHandler(30, 0, 0));
                        // 添加HTTP编解码器
                        pipeline.addLast(new HttpServerCodec());
                        // 添加分块写处理器，支持HTTP的分块传输编码
                        pipeline.addLast(new ChunkedWriteHandler());
                        /**
                         * 说明：
                         *  1. http数据在传输过程中是分段的，HttpObjectAggregator可以把多个段聚合起来；
                         *  2. 这就是为什么当浏览器发送大量数据时，就会发出多次 http请求的原因
                         */
                        // 添加HTTP消息聚合器，聚合HTTP消息的各个部分
                        pipeline.addLast(new HttpObjectAggregator(8192));
                        //保存用户ip
                        // pipeline.addLast(new HttpHeadersHandler());

                        //
                        pipeline.addLast(new MyHeaderCollectHandler());
                        /**
                         * 说明：
                         *  1. 对于 WebSocket，它的数据是以帧frame 的形式传递的；
                         *  2. 可以看到 WebSocketFrame 下面有6个子类
                         *  3. 浏览器发送请求时： ws://localhost:7000/hello 表示请求的uri
                         *  4. WebSocketServerProtocolHandler 核心功能是把 http协议升级为 ws 协议，保持长连接；
                         *      是通过一个状态码 101 来切换的
                         */
                        // 添加WebSocket协议处理器
                        pipeline.addLast(new WebSocketServerProtocolHandler("/"));
                        // 添加自定义的处理器，用于处理WebSocket的业务逻辑
                        pipeline.addLast(NETTY_WEB_SOCKET_SERVER_HANDLER);
                    }
                });

        // 绑定端口并开始接受连接  ，启动服务器，监听端口
        serverBootstrap.bind(WEB_SOCKET_PORT).sync();

        // 绑定端口并开始接受连接  ，启动服务器，监听端口，阻塞直到启动成功
//        ChannelFuture future = serverBootstrap.bind(WEB_SOCKET_PORT).sync();
//        future.channel().closeFuture().sync(); // 同步等待服务器Socket关闭
    }

    /**
     * 使用@PreDestroy注解标记的方法，在对象销毁之前自动执行
     */
    @PreDestroy
    public void destroy() {
        // 优雅地关闭bossGroup线程池，并等待其关闭完成
        Future<?> future = bossGroup.shutdownGracefully();
        // 优雅地关闭workerGroup线程池，并等待其关闭完成
        Future<?> future1 = workerGroup.shutdownGracefully();
        // 同步等待bossGroup关闭完成
        future.syncUninterruptibly();
        // 同步等待workerGroup关闭完成
        future1.syncUninterruptibly();
        // 打印日志，表明服务器已成功关闭
        log.info("关闭 ws server 成功");
    }
}
